var line = {};

if (typeof(module) != 'undefined') {
    module.exports = line;
}

(function () {
    var toString = Object.prototype.toString;
    
    line.extend = function (target, source) {
        for (var prop in source) {
            target[prop] = source[prop];
        }
        return target;
    };

    line.extend(line, {
        extend:function (target, source) {
            for (var prop in source) {
                target[prop] = source[prop];
            }
            return target;
        },
        each:function (target, callback, context, reverse) {
            if (line.isCollection(target)) {
                var i , len = target.length;
                if (!reverse) {
                    for (i = 0; i < len; i++) {
                        if (callback.call(context || this, target[i], i, target) === false) {
                            return i;
                        }
                    }
                } else {
                    for (i = len - 1; i >= 0; i--) {
                        if (callback.call(context || this, target[i], i, target) === false) {
                            return i;
                        }
                    }
                }
            } else {
                for (var key in target) {
                    if (!line.has(target, key)) {
                        continue;
                    }
                    if (callback.call(context || this, target[key], key, target) === false) {
                        return key;
                    }
                }
            }
        },
        compare:function (target1, target2) {
            var type = line.getType(target1),
                result;
            switch (type) {
                case 'String':
                    result = target1.localeCompare(target2);
                    break;
                case 'Number':
                case 'Boolean':
                case 'Date':
                    result = target1 - target2;
                    break;
            }

            return result && result / Math.abs(result);
        },
        clone:function (target) {
            return line.extend({}, target);
        },
        is:function (target, type) {
            return type === line.getType(target);
        },
        isUndefined:function (target) {
            return line.is(target, 'Undefined');
        },
        isNull:function (target) {
            return line.is(target, 'Null');
        },
        isString:function (target) {
            return line.is(target, 'String');
        },
        isNumber:function (target) {
            return line.is(target, 'Number') && !isNaN(target);
        },
        isBoolean:function (target) {
            return line.is(target, 'Boolean');
        },
        isObject:function (target) {
            return line.is(target, 'Object');
        },
        isArray:Array.isArray || function (target) {
            return line.is(target, 'Array');
        },
        isFunction:function (target) {
            return line.is(target, 'Function');
        },
        isCollection:function (target) {
            return target && target.length >= 0;
        },
        isEmpty:function (target) {
            switch (line.getType(target)) {
                case 'Null':
                    return true;
                case 'Array':
                case 'String':
                    return target.length === 0;
                case 'Object':
                    return line.isEmptyObject(target);
            }
        },
        getType:function (target) {
            if (target === null) {
                return 'Null';
            }
            if (target === void 0) {
                return 'Undefined'
            }

            return toString.call(target).slice(8, -1);
        },
        isEmptyObject:function (target) {
            for (var key in target) {
                return false;
            }
            return true;
        },
        has:function (obj, prop) {
            return obj.hasOwnProperty(prop);
        },
        override:function (target, name, fn) {
            var oldFn = target[name];
            return oldFn && (target[name] = fn(oldFn));
        }
    });
})();